home *** CD-ROM | disk | FTP | other *** search
/ Reverse Code Engineering RCE CD +sandman 2000 / ReverseCodeEngineeringRceCdsandman2000.iso / RCE / Library / Assembly Programming Journal / apj_5.txt < prev    next >
Encoding:
Text File  |  2000-05-25  |  32.0 KB  |  948 lines

  1. ::/ \::::::.
  2. :/___\:::::::.
  3. /|    \::::::::.
  4. :|   _/\:::::::::.
  5. :| _|\  \::::::::::.                                               July-Sep  99
  6. :::\_____\::::::::::.                                              Issue      5
  7. ::::::::::::::::::::::.........................................................
  8.  
  9.             A S S E M B L Y   P R O G R A M M I N G   J O U R N A L
  10.                       http://asmjournal.freeservers.com
  11.                            asmjournal@mailcity.com
  12.  
  13.  
  14.  
  15.  
  16. T A B L E   O F   C O N T E N T S
  17. ----------------------------------------------------------------------
  18. Introduction...................................................mammon_
  19.  
  20. "COM in Assembly Part II"...................................Bill.Tyler
  21.  
  22. "How to use DirectDraw in ASM"...............................X-Calibre
  23.  
  24. "Writing Boot Sectors To Disk"...........................Jan.Verhoeven
  25.  
  26. "Dumping Memory to Disk".................................Jan.Verhoeven
  27.  
  28. "Formatted Numeric Output"..............................Laura.Fairhead
  29.  
  30. "Linked Lists in ASM"..........................................mammon_
  31.  
  32. Column: Win32 Assembly Programming
  33.     "Structured Exception Handling under Win32"...........Chris.Dragan
  34.     "Child Window Controls"...................................Iczelion
  35.     "Dialog Box as Main Window"...............................Iczelion
  36.     "Standardizing Win32 Callback Procedures"............Jeremy.Gordon
  37.  
  38. Column: The Unix World
  39.     "Fire Demo ported to Linux SVGAlib".................Jan.Wagemakers
  40.  
  41. Column: Assembly Language Snippets
  42.     "Abs".................................................Chris.Dragan
  43.     "Min".................................................Chris.Dragan
  44.     "Max".................................................Chris.Dragan
  45.     "OBJECT"...................................................mammon_
  46.  
  47. Column: Issue Solution
  48.     "Binary to ASCII"....................................Jan.Verhoeven
  49.  
  50. ----------------------------------------------------------------------
  51.        ++++++++++++++++++Issue  Challenge+++++++++++++++++
  52.          Convert a bit value to ACIII less than 10 bytes
  53. ----------------------------------------------------------------------
  54.  
  55.  
  56.  
  57. ::/ \::::::.
  58. :/___\:::::::.
  59. /|    \::::::::.
  60. :|   _/\:::::::::.
  61. :| _|\  \::::::::::.
  62. :::\_____\:::::::::::..............................................INTRODUCTION
  63.                                                                      by mammon_
  64.  
  65.  
  66. I suppose I should start with the good news. A week or so ago Hiroshimator
  67. emailed me for the nth time asking if I needed help with the journal as I have
  68. yet to get one out on time. I relented and asked if he knew any listservers;
  69. one hour later he had an account for APJ set up at e-groups, specifically:
  70.              http://www.egroups.com/group/apj-announce
  71. One of the greatest obstacles to putting out these issues -- processing the
  72. 300 or so subscription requests that rack up between issues -- is now out of
  73. the way for good.
  74.  
  75. The articles this month have somewhat of a high-level focus; with the COM and
  76. Direct Draw by Bill Tyler and X-Caliber, respectively, as well as Chris
  77. Dragan's classic work on exception handling and Jeremy Gordon's treatment of
  78. windows callbacks, this issue is heavily weighed towards high-level win32
  79. coding. Add to this Iczelion's two tutorials and my own win32-biased
  80. linked list example, and it appears the DOS/Unix camp is losing ground.
  81.  
  82. To shore up the Unix front line, Jan Wagemakers has provided a port of last
  83. month's fire demo to linux [GAS]. In addition, there are A86 articles by Jan
  84. Verhoeven and a general assembly routine by Laura Fairhead to prove that not
  85. all assembly has to be 32-bit.
  86.  
  87. And, finally, I am looking for a good 'challenge' columnist: someone to write
  88. the monthly APJ challenges [and their solutions] so that I can start
  89. announcing next month's challenge sooner than next month...
  90.  
  91. Now at last I can sleep ;)
  92.  
  93. _m
  94.  
  95.  
  96. ::/ \::::::.
  97. :/___\:::::::.
  98. /|    \::::::::.
  99. :|   _/\:::::::::.
  100. :| _|\  \::::::::::.
  101. :::\_____\:::::::::::...........................................FEATURE.ARTICLE
  102.                                                         COM in Assembly Part II
  103.                                 by Bill Tyler
  104.  
  105.  
  106. My previous atricle described how to use COM objects in your assembly
  107. language programs.  It described only how to call COM methods, but not how to
  108. create your own COM objects.  This article will describe how to do that.
  109.  
  110. This article will describe implementing COM Objects, using MASM syntax.  TASM
  111. or NASM assemblers will not be considered, however the methods can be easily
  112. applied to any assembler.
  113.  
  114. This article will also not describe some of the more advanced features of COM
  115. such as reuse, threading, servers/clients, and so on.  These will presented
  116. in future articles.
  117.  
  118.  
  119. COM Interfaces Review
  120. ------------------------------------------------------------------------------
  121. An interface definition specifies the interface's methods, their return types,
  122. the number and types of their parameters, and what the methods must do.  Here
  123. is a sample interface definition:
  124.  
  125. IInterface struct
  126.     lpVtbl  dd  ?
  127. IInterface ends
  128.  
  129. IInterfaceVtbl struct
  130.     ; IUnknown methods
  131.     STDMETHOD       QueryInterface, :DWORD, :DWORD, :DWORD
  132.     STDMETHOD       AddRef, :DWORD
  133.     STDMETHOD       Release, :DWORD
  134.     ; IInterface methods
  135.     STDMETHOD       Method1, :DWORD
  136.     STDMETHOD       Method2, :DWORD
  137. IInterfaceVtbl ends
  138.  
  139. STDMETHOD is used to simplify the interface declaration, and is defined as:
  140.  
  141. STDMETHOD MACRO name, argl :VARARG
  142.     LOCAL @tmp_a
  143.     LOCAL @tmp_b
  144.     @tmp_a TYPEDEF PROTO argl
  145.     @tmp_b TYPEDEF PTR @tmp_a
  146.     name @tmp_b ?
  147. ENDM
  148.  
  149. This macro is used to greatly simplify interface declarations, and so that the
  150. MASM invoke syntax can be used. (Macro originally by Ewald :)
  151.  
  152. Access to the interface's methods occurs through a pointer.  This pointer
  153. points to a table of function pointers, called a vtable. Here is a sample
  154. method call:
  155.  
  156. mov     eax, [lpif]                            ; lpif is the interface pointer
  157. mov     eax, [eax]                             ; get the address of the vtable
  158. invoke  (IInterfaceVtbl [eax]).Method1, [lpif] ; indirect call to the function
  159. - or -
  160. invoke  [eax][IInterfaceVtbl.Method2], [lpif]  ; alternate notation
  161.  
  162. Two different styles of addressing the members are shown.  Both notations
  163. produce equivalent code, so the method used is a matter of personal
  164. preference.
  165.  
  166. All interfaces must inherit from the IUnknown interface.  This means that the
  167. first 3 methods of the vtable must be QueryInterface, AddRef, and Release.
  168. The purpose and implementation of these methods will be discussed later.
  169.  
  170.  
  171. GUIDS
  172. ------------------------------------------------------------------------------
  173. A GUID is a Globally Unique ID.  A GUID is a 16-byte number, that is unique
  174. to an interface.  COM uses GUID's to identify different interfaces from one
  175. another.  Using this method prevents name clashing as well as version
  176. clashing.  To get a GUID, you use a generator utility that is included with
  177. most win32 development packages.
  178.  
  179. A GUID is represented by the following structure:
  180.  
  181. GUID STRUCT
  182.     Data1   dd ?
  183.     Data2   dw ?
  184.     Data3   dw ?
  185.     Data4   db 8 dup(?)
  186. GUID ENDS
  187.  
  188. A GUID is then defined in the data section:
  189. MyGUID GUID <3F2504E0h, 4f89h, 11D3h, <9Ah, 0C3h, 0h, 0h, 0E8h, 2Ch, 3h, 1h>>
  190.  
  191. Once a GUID is assigned to an interface and published, no furthur changes to
  192. the interface definition are allowed.  Note, that this does mean that the
  193. interface implementation may not change, only the definition.  For changes
  194. to the interface definition, a new GUID must be assigned.
  195.  
  196.  
  197. COM Objects
  198. ------------------------------------------------------------------------------
  199. A COM object is simply an implementation of an interface.  Implementation
  200. details are not covered by the COM standard, so we are free to implement our
  201. objects as we choose, so long as they satisfy all the requirements of the
  202. interface definition.
  203.  
  204. A typical object will contain pointers to the various interfaces it supports,
  205. a reference count, and any other data that the object needs.  Here is a sample
  206. object definition, implemented as a structure:
  207.  
  208. Object struct
  209.     interface   IInterface  <?>     ; pointer to an IInterface
  210.     nRefCount   dd          ?       ; reference count
  211.     nValue      dd          ?       ; private object data
  212. Object ends
  213.  
  214. We also have to define the vtable's we are going to be using.  These tables
  215. must be static, and cannot change during run-time.  Each member of the vtable
  216. is a pointer to a method.  Following is a method for defining the vtable.
  217.  
  218. @@IInterface segment dword
  219. vtblIInterface:
  220.     dd      offset IInterface@QueryInterface
  221.     dd      offset IInterface@AddRef
  222.     dd      offset IInterface@Release
  223.     dd      offset IInterface@GetValue
  224.     dd      offset IInterface@SetValue
  225. @@IInterface ends
  226.  
  227.  
  228. Reference Counting
  229. ------------------------------------------------------------------------------
  230. COM object manage their lifetimes through reference counting.  Each object
  231. maintains a reference count that keeps track of how many instances of the
  232. interface pointer have been created.  The object is required to keep a
  233. counter that supports 2^32 instances, meaning the reference count must be a
  234. DWORD.
  235.  
  236. When the reference count drops to zero, the object is no longer in use, and
  237. it destroys itself.  The 2 IUnknown methods AddRef and Release handle the
  238. reference counting for a COM object.
  239.  
  240.  
  241. QueryInterface
  242. ------------------------------------------------------------------------------
  243. The QueryInterface method is used by a COM object to determine if the object
  244. supports a given interface, and then if supported, to get the interface
  245. pointer.  There are 3 rules to implementing the QueryInterface method:
  246.  
  247.     1. Objects must have an identity - a call to QueryInterface must always
  248.        return the same pointer value.
  249.     2. The set of interfaces of an object must never change - for example, if
  250.        a call to QueryInterface with on IID succeeds once, it must succeed
  251.        always.  Likewise, if it fails once, it must fail always.
  252.     3. It must be possible to successfully query an interface of an object
  253.        from any other interface.
  254.  
  255. QueryInterface returns a pointer to a specified interface on an object to
  256. which a client currently holds an interface pointer. This function must call
  257. the AddRef method on the pointer it returns.
  258.  
  259. Following are the QueryInterface parameters:
  260.     pif  : [in] a pointer to the calling interface
  261.     riid : [in] pointer to the IID of the interface being queried
  262.     ppv  : [out] pointer to the pointer of the interface that is to be set.
  263.            If the interface is not supported, the pointed to value is set to 0
  264.  
  265. QueryInterface returns the following:
  266.    S_OK if the interface is supported
  267.    E_NOINTERFACE if not supported
  268.  
  269. Here is a simple assembly implementation of QueryInterface:
  270.  
  271. IInterface@QueryInterface proc uses ebx pif:DWORD, riid:DWORD, ppv:DWORD
  272.     ; The following compares the requested IID with the available ones.
  273.     ; In this case, because IInterface inherits from IUnknown, the IInterface
  274.     ; interface is prefixed with the IUnknown methods, and these 2 interfaces
  275.     ; share the same interface pointer.
  276.     invoke  IsEqualGUID, [riid], addr IID_IInterface
  277.     or      eax,eax
  278.     jnz     @1
  279.     invoke  IsEqualGUID, [riid], addr IID_IUnknown
  280.     or      eax,eax
  281.     jnz     @1
  282.     jmp     @NoInterface
  283.  
  284. @1:
  285.     ; GETOBJECTPOINTER is a macro that will put the object pointer into eax,
  286.     ; when given the name of the object, the name of the interface, and the
  287.     ; interface pointer.
  288.     GETOBJECTPOINTER    Object, interface, pif
  289.  
  290.     ; now get the pointer to the requested interface
  291.     lea     eax, (Object ptr [eax]).interface
  292.  
  293.     ; set *ppv with this interface pointer
  294.     mov     ebx, [ppv]
  295.     mov     dword ptr [ebx], eax
  296.  
  297.     ; increment the reference count by calling AddRef
  298.     GETOBJECTPOINTER    Object, interface, pif
  299.     mov     eax, (Object ptr [eax]).interface
  300.     invoke  (IInterfaceVtbl ptr [eax]).AddRef, pif
  301.  
  302.     ; return S_OK
  303.     mov     eax, S_OK
  304.     jmp     return
  305.  
  306. @NoInterface:
  307.     ; interface not supported, so set *ppv to zero
  308.     mov     eax, [ppv]
  309.     mov     dword ptr [eax], 0
  310.  
  311.     ; return E_NOINTERFACE
  312.     mov     eax, E_NOINTERFACE
  313.  
  314. return:
  315.     ret
  316. IInterface@QueryInterface endp
  317.  
  318.  
  319. AddRef
  320. ------------------------------------------------------------------------------
  321. The AddRef method is used to increment the reference count for an interface
  322. of an object.  It should be called for every new copy of an interface pointer
  323. to an object.
  324.  
  325. AddRef takes no parameters, other than the interface pointer required for all
  326. methods.  AddRef should return the new reference count.  However, this value
  327. is to be used by callers only for testing purposes, as it may be unstable in
  328. certain situations.
  329.  
  330. Following is a simple implementation of the AddRef method:
  331.  
  332. IInterface@AddRef proc pif:DWORD
  333.     GETOBJECTPOINTER    Object, interface, pif
  334.     ; increment the reference count
  335.     inc     [(Object ptr [eax]).nRefCount]
  336.     ; now return the count
  337.     mov     eax, [(Object ptr [eax]).nRefCount]
  338.     ret
  339. IInterface@AddRef endp
  340.  
  341.  
  342. Release
  343. ------------------------------------------------------------------------------
  344. Release decrements the reference count for the calling interface on a object.
  345. If the reference count on the object is decrememnted to 0, then the object is
  346. freed from memory.  This function should be called when you no longer need to
  347. use an interface pointer
  348.  
  349. Like AddRef, Release takes only one parameter - the interface pointer.  It
  350. also returns the current value of the reference count, which, similarly, is to
  351. be used for testing purposess only
  352.  
  353. Here is a simple implementation of Release:
  354.  
  355. IInterface@Release proc pif:DWORD
  356.     GETOBJECTPOINTER    Object, interface, pif
  357.  
  358.     ; decrement the reference count
  359.     dec     [(Object ptr [eax]).nRefCount]
  360.  
  361.     ; check to see if the reference count is zero.  If it is, then destroy
  362.     ; the object.
  363.     mov     eax, [(Object ptr [eax]).nRefCount]
  364.     or      eax, eax
  365.     jnz     @1
  366.  
  367.     ; free the object: here we have assumed the object was allocated with
  368.     ; LocalAlloc and with LMEM_FIXED option
  369.     GETOBJECTPOINTER    Object, interface, pif
  370.     invoke  LocalFree, eax
  371. @1:
  372.     ret
  373. IInterface@Release endp
  374.  
  375.  
  376. Creating a COM object
  377. ------------------------------------------------------------------------------
  378. Creating an object consists basically of allocating the memory for the
  379. object, and then initializing its data members.  Typically, the vtable
  380. pointer is initialized and the reference count is zeroed.  QueryInterface
  381. could then be called to get the interface pointer.
  382.  
  383. Other methods exist for creating objects, such as using CoCreateInstance, and
  384. using class factories.  These methods will not be discussed, and may be a
  385. topic for a future article.
  386.  
  387.  
  388. COM implementatiion sample application
  389. ------------------------------------------------------------------------------
  390. Here follows a sample implementation and usage of a COM object.  It shows how
  391. to create the object, call its methods, then free it.  It would probably be
  392. very educational to assemble this and run it through a debugger.  This and
  393. other examples can be found at http://asm.tsx.org.
  394.  
  395.  
  396. .386
  397. .model flat,stdcall
  398.  
  399. include windows.inc
  400. include kernel32.inc
  401. include user32.inc
  402.  
  403. includelib kernel32.lib
  404. includelib user32.lib
  405. includelib uuid.lib
  406.  
  407. ;-----------------------------------------------------------------------------
  408. ; Macro to simply interface declarations
  409. ; Borrowed from Ewald, http://here.is/diamond/
  410. STDMETHOD   MACRO   name, argl :VARARG
  411. LOCAL @tmp_a
  412. LOCAL @tmp_b
  413. @tmp_a  TYPEDEF PROTO argl
  414. @tmp_b  TYPEDEF PTR @tmp_a
  415. name    @tmp_b      ?
  416. ENDM
  417.  
  418. ; Macro that takes an interface pointer and returns the implementation
  419. ; pointer in eax
  420. GETOBJECTPOINTER MACRO Object, Interface, pif
  421.     mov     eax, pif
  422.     IF (Object.Interface)
  423.         sub     eax, Object.Interface
  424.     ENDIF
  425. ENDM
  426.  
  427. ;-----------------------------------------------------------------------------
  428. IInterface@QueryInterface   proto :DWORD, :DWORD, :DWORD
  429. IInterface@AddRef           proto :DWORD
  430. IInterface@Release          proto :DWORD
  431. IInterface@Get              proto :DWORD
  432. IInterface@Set              proto :DWORD, :DWORD
  433.  
  434. CreateObject                proto :DWORD
  435. IsEqualGUID                 proto :DWORD, :DWORD
  436.  
  437. externdef                   IID_IUnknown:GUID
  438.  
  439. ;-----------------------------------------------------------------------------
  440. ; declare the interface prototype
  441. IInterface struct
  442.     lpVtbl  dd  ?
  443. IInterface ends
  444.  
  445. IInterfaceVtbl struct
  446.     ; IUnknown methods
  447.     STDMETHOD       QueryInterface, pif:DWORD, riid:DWORD, ppv:DWORD
  448.     STDMETHOD       AddRef, pif:DWORD
  449.     STDMETHOD       Release, pif:DWORD
  450.     ; IInterface methods
  451.     STDMETHOD       GetValue, pif:DWORD
  452.     STDMETHOD       SetValue, pif:DWORD, val:DWORD
  453. IInterfaceVtbl ends
  454.  
  455.  
  456. ; declare the object structure
  457. Object struct
  458.     ; interface object
  459.     interface   IInterface  <?>
  460.  
  461.     ; object data
  462.     nRefCount   dd          ?
  463.     nValue      dd          ?
  464. Object ends
  465.  
  466. ;-----------------------------------------------------------------------------
  467. .data
  468. ; define the vtable
  469. @@IInterface segment dword
  470. vtblIInterface:
  471.     dd      offset IInterface@QueryInterface
  472.     dd      offset IInterface@AddRef
  473.     dd      offset IInterface@Release
  474.     dd      offset IInterface@GetValue
  475.     dd      offset IInterface@SetValue
  476. @@IInterface ends
  477.  
  478. ; define the interface's IID
  479. ; {CF2504E0-4F89-11d3-9AC3-0000E82C0301}
  480. IID_IInterface GUID <0cf2504e0h, 04f89h, 011d3h, <09ah, 0c3h, 00h, 00h,
  481.                       0e8h, 02ch, 03h, 01h>>
  482.  
  483. ;-----------------------------------------------------------------------------
  484. .code
  485. start:
  486. StartProc proc
  487.     LOCAL   pif:DWORD       ; interface pointer
  488.  
  489.     ; create the object
  490.     invoke  CreateObject, addr [pif]
  491.     or      eax,eax
  492.     js      exit
  493.  
  494.     ; call the SetValue method
  495.     mov     eax, [pif]
  496.     mov     eax, [eax]
  497.     invoke  (IInterfaceVtbl ptr [eax]).SetValue, [pif], 12345h
  498.  
  499.     ; call the GetValue method
  500.     mov     eax, [pif]
  501.     mov     eax, [eax]
  502.     invoke  (IInterfaceVtbl ptr [eax]).GetValue, [pif]
  503.  
  504.     ; release the object
  505.     mov     eax, [pif]
  506.     mov     eax, [eax]
  507.     invoke  (IInterfaceVtbl ptr [eax]).Release, [pif]
  508.  
  509. exit:
  510.     ret
  511. StartProc endp
  512.  
  513. ;-----------------------------------------------------------------------------
  514. IInterface@QueryInterface proc uses ebx pif:DWORD, riid:DWORD, ppv:DWORD
  515.     invoke  IsEqualGUID, [riid], addr IID_IInterface
  516.     test    eax,eax
  517.     jnz     @F
  518.     invoke  IsEqualGUID, [riid], addr IID_IUnknown
  519.     test    eax,eax
  520.     jnz     @F
  521.     jmp     @Error
  522.  
  523. @@:
  524.     GETOBJECTPOINTER    Object, interface, pif
  525.     lea     eax, (Object ptr [eax]).interface
  526.  
  527.     ; set *ppv
  528.     mov     ebx, [ppv]
  529.     mov     dword ptr [ebx], eax
  530.  
  531.     ; increment the reference count
  532.     GETOBJECTPOINTER    Object, interface, pif
  533.     mov     eax, (Object ptr [eax]).interface
  534.     invoke  (IInterfaceVtbl ptr [eax]).AddRef, [pif]
  535.  
  536.     ; return S_OK
  537.     mov     eax, S_OK
  538.     jmp     return
  539.  
  540. @Error:
  541.     ; error, interface not supported
  542.     mov     eax, [ppv]
  543.     mov     dword ptr [eax], 0
  544.     mov     eax, E_NOINTERFACE
  545.  
  546. return:
  547.     ret
  548. IInterface@QueryInterface endp
  549.  
  550.  
  551. IInterface@AddRef proc pif:DWORD
  552.     GETOBJECTPOINTER    Object, interface, pif
  553.     inc     [(Object ptr [eax]).nRefCount]
  554.     mov     eax, [(Object ptr [eax]).nRefCount]
  555.     ret
  556. IInterface@AddRef endp
  557.  
  558.  
  559. IInterface@Release proc pif:DWORD
  560.     GETOBJECTPOINTER    Object, interface, pif
  561.     dec     [(Object ptr [eax]).nRefCount]
  562.     mov     eax, [(Object ptr [eax]).nRefCount]
  563.     or      eax, eax
  564.     jnz     @1
  565.     ; free object
  566.     mov     eax, [pif]
  567.     mov     eax, [eax]
  568.     invoke  LocalFree, eax
  569. @1:
  570.     ret
  571. IInterface@Release endp
  572.  
  573.  
  574. IInterface@GetValue proc pif:DWORD
  575.     GETOBJECTPOINTER    Object, interface, pif
  576.     mov     eax, (Object ptr [eax]).nValue
  577.     ret
  578. IInterface@GetValue endp
  579.  
  580.  
  581. IInterface@SetValue proc uses ebx pif:DWORD, val:DWORD
  582.     GETOBJECTPOINTER    Object, interface, pif
  583.     mov     ebx, eax
  584.     mov     eax, [val]
  585.     mov     (Object ptr [ebx]).nValue, eax
  586.     ret
  587. IInterface@SetValue endp
  588.  
  589. ;-----------------------------------------------------------------------------
  590. CreateObject proc uses ebx ecx pobj:DWORD
  591.     ; set *ppv to 0
  592.     mov     eax, pobj
  593.     mov     dword ptr [eax], 0
  594.  
  595.     ; allocate object
  596.     invoke  LocalAlloc, LMEM_FIXED, sizeof Object
  597.     or      eax, eax
  598.     jnz     @1
  599.     ; alloc failed, so return
  600.     mov     eax, E_OUTOFMEMORY
  601.     jmp     return
  602. @1:
  603.  
  604.     mov     ebx, eax
  605.     mov     (Object ptr [ebx]).interface.lpVtbl, offset vtblIInterface
  606.     mov     (Object ptr [ebx]).nRefCount, 0
  607.     mov     (Object ptr [ebx]).nValue, 0
  608.  
  609.     ; Query the interface
  610.     lea     ecx, (Object ptr [ebx]).interface
  611.     mov     eax, (Object ptr [ebx]).interface.lpVtbl
  612.     invoke  (IInterfaceVtbl ptr [eax]).QueryInterface,
  613.             ecx,
  614.             addr IID_IInterface,
  615.             [pobj]
  616.     cmp     eax, S_OK
  617.     je      return
  618.  
  619.     ; error in QueryInterface, so free memory
  620.     push    eax
  621.     invoke  LocalFree, ebx
  622.     pop     eax
  623.  
  624. return:
  625.     ret
  626. CreateObject endp
  627.  
  628. ;-----------------------------------------------------------------------------
  629. IsEqualGUID proc rguid1:DWORD, rguid2:DWORD
  630.     cld
  631.     mov     esi, [rguid1]
  632.     mov     edi, [rguid2]
  633.     mov     ecx, sizeof GUID / 4
  634.     repe    cmpsd
  635.     xor     eax, eax
  636.     or      ecx, ecx
  637.     setz    al
  638.     ret
  639. IsEqualGUID endp
  640.  
  641. end start
  642.  
  643.  
  644. Conclusion
  645. ------------------------------------------------------------------------------
  646. We have (hopefully) seen how to implement a COM object.  We can see that it
  647. is a bit messy to do, and adds quite some overhead to our programs.  However,
  648. it can also add great flexibility and power to our programs.
  649.  
  650. Remember that COM defines only interfaces, and implementation is left to the
  651. programmer.  This article presents only one possible implementation.  This is
  652. not the only method, nor is it the best one.  The reader should feel free to
  653. experiment with other methods.
  654.  
  655.                 Copyright (C) 1999 Bill Tyler  (billasm@usa.net)
  656.  
  657.  
  658.  
  659. ::/ \::::::.
  660. :/___\:::::::.
  661. /|    \::::::::.
  662. :|   _/\:::::::::.
  663. :| _|\  \::::::::::.
  664. :::\_____\:::::::::::..........................................FEATURE.ARTICLE
  665.                                                   How to use DirectDraw in ASM
  666.                                                   by X-Calibre [Diamond]
  667.  
  668.  
  669. Well, there has been quite a large demand for this essay, so I finally started
  670. writing it. This essay will show you how to use C++ objects and COM interface
  671. in Win32ASM, using DirectDraw as an example.
  672.  
  673. Well, in this part of the Win32 API, you will soon find out how important it
  674. is to know C and C++ when you want to use an API written in these languages.
  675. Judging from the demand for this essay, I think it will be necessary to
  676. explain a bit of how objects work in C++. I will not go too deep, but only
  677. show the things you need to know in Win32ASM.
  678.  
  679. What are objects really?
  680.  
  681. Actually a structure is an object of which all fields are public. We will look
  682. at it the other way around. So the public fields in an object make up a
  683. structure. The other fields in an object are private and are not reachable
  684. from the outside. So they are not interesting to us.
  685.  
  686. A special thing about objects is that they can contain pointers to functions.
  687. Normally, when using C or ASM, this would be possible, but a bit error-prone.
  688. It can be seen as 'dirty' programming. That's why you probably haven't seen it
  689. before.
  690.  
  691. When using C++ with a compiler, there will be no errors, as long as the
  692. compiler does its job. So here you can use this technique with no chance of
  693. errors, and it gives you some nice new programming options.
  694.  
  695. C++ goes even further with this 'structure of functions' idea. With
  696. inheritance, you can also overwrite functions of the base class in the
  697. inherited class. You can also create 'virtual' functions, which are defined in
  698. the base class, but the actual code is only in inherited classes.
  699.  
  700. This is of course interesting for DirectX, where you want to have standard
  701. functions, but with different code, depending on the hardware on which it is
  702. running. So in DirectX, all functions are defined as virtual, and the base
  703. class is inherited by hardware-specific drivers which supply hardware-specific
  704. code. And the beauty of this is, that it's all transparent to the programmer.
  705. The function pointers can change at runtime because of this system, so the C++
  706. designers had to think of a way to keep the pointers to the functions
  707. available to the program at all time.
  708.  
  709. What this all boils down to is that there is a table with pointers to the
  710. functions. It's called the Virtual Function Table. I will call this the
  711. vtable from now on.
  712.  
  713. So we need to get this table, in order to call functions from our object.
  714. Lucky for you, Z-Nith has already made a C program to 'capture' the table,
  715. and converted the resulting header file to an include file for use with MASM.
  716. So I'll just explain how you should use this table, and you can get going
  717. soon.
  718.  
  719. Well, actually it's quite simple. The DirectX objects are defined like this:
  720.  
  721. IDirectDraw        STRUC
  722.     lpVtbl DWORD ?
  723. IDirectDraw        ENDS
  724.  
  725. IDirectDrawPalette STRUC
  726.     lpVtbl DWORD ?
  727. IDirectDrawPalette ENDS
  728.  
  729. IDirectDrawClipper STRUC
  730.     lpVtbl DWORD ?
  731. IDirectDrawClipper ENDS
  732.  
  733. IDirectDrawSurface STRUC
  734.     lpVtbl DWORD ?
  735. IDirectDrawSurface ENDS
  736.  
  737. So these structs are actually just a pointer to the vtables, and don't contain
  738. any other values. Well, this makes it all very easy for us then.
  739. I'll give you a small example:
  740.  
  741. Say we have an IDirectDraw object called lpDD. And we want to call the
  742. RestoreDisplayMode function.
  743. Then we need to do 2 things:
  744.  
  745. 1. Get the vtable.
  746. 2. Get the address of the function, using the vtable.
  747.  
  748. The first part is simple. All the struct contains, is the pointer to the
  749. vtable. So we can just do this:
  750.  
  751.     mov  eax, [lpDD]
  752.     mov  eax, [eax]
  753.  
  754. Simple, isn't it? And the next part isn't really much harder. The vtable is
  755. put into a structure called IDirectDrawVtbl in DDRAW.INC. We now have the
  756. address of the structure in eax. All we have to do now, is get the correct
  757. member of that structure, to get the address of the function we want to call.
  758. You would have guessed by now, that this will do the trick:
  759.  
  760.     call [IDirectDrawVtbl.RestoreDisplayMode][eax]
  761.  
  762. That is not a bad guess...
  763. But there's one more thing, which is very important: this function needs to be
  764. invoked on the IDirectDraw object. We may only see the vtable in the structure,
  765. but there are also private members inside the object. So there's more than
  766. meets the eye here. What it comes down to is that the call needs the object
  767. as an argument. And this will be done by stack as always. So we just need to
  768. push lpDD before we call. The complete call will look like this:
  769.  
  770.     push [lpDD]
  771.     call [IDirectDrawVtbl.RestoreDisplayMode][eax]
  772.  
  773. Simple, was it not? And calls with arguments are not much harder.
  774. Let's set the displaymode to 320x200 in 32 bits next.
  775. This call requires 3 arguments:
  776.  
  777. SetDisplayMode( width, height, bpp );
  778.  
  779. Well, the extra arguments work just like normal API calls: just push them onto
  780. the stack in backward order.
  781. So it will look like this:
  782.  
  783.     push 32
  784.     push 200
  785.     push 320
  786.     mov  eax, [lpDD]
  787.     push eax
  788.     mov  eax, [eax]
  789.     call [IDirectDrawVtbl.SetDisplayMode][eax]
  790.  
  791. And that's all there is to it.
  792.  
  793. To make life easier, we have included some MASM macros in DDRAW.INC, for use
  794. with the IDirectDraw and IDirectDrawSurface objects:
  795.  
  796. DDINVOKE  MACRO   func, this, arglist :VARARG
  797.     mov  eax, [this]
  798.     mov  eax, [eax]
  799.  
  800.     IFB <arglist>
  801.         INVOKE [IDirectDrawVtbl. func][eax], this
  802.     ELSE
  803.         INVOKE [IDirectDrawVtbl. func][eax], this, arglist
  804.     ENDIF
  805. ENDM
  806.  
  807. DDSINVOKE MACRO   func, this, arglist :VARARG
  808.     mov  eax, [this]
  809.     mov  eax,  [eax]
  810.  
  811.     IFB <arglist>
  812.         INVOKE [IDirectDrawSurfaceVtbl. func][eax], this
  813.     ELSE
  814.         INVOKE [IDirectDrawSurfaceVtbl. func][eax], this, arglist
  815.     ENDIF
  816. ENDM
  817.  
  818. With these macros, our 2 example calls will look as simple as this:
  819.  
  820.     DDINVOKE RestoreDisplayMode, lpDD
  821.  
  822.     DDINVOKE SetDisplayMode, lpDD, 320, 200, 32
  823.  
  824.  
  825. Well, that's basically all there is to know about using objects, COM and
  826. DirectX in Win32ASM. Have fun with it!
  827.  
  828. And remember:
  829.  
  830. C and C++ knowledge is power!
  831.  
  832.  
  833.  
  834. ::/ \::::::.
  835. :/___\:::::::.
  836. /|    \::::::::.
  837. :|   _/\:::::::::.
  838. :| _|\  \::::::::::.
  839. :::\_____\:::::::::::...........................................FEATURE.ARTICLE
  840.                                                    Writing Boot Sectors To Disk
  841.                                                    by Jan Verhoeven
  842.  
  843.  
  844. Introduction.
  845. -------------
  846. In my previous article I showed how to make a private non-bootable
  847. bootsector for 1.44 Mb floppy disks. Unfortunately, there was no way yet to
  848. write that non-bootsector to a floppy disk....
  849.  
  850. Enter this code. It is the accompanying bootsector writer for floppy disks.
  851. It assumes that your A: drive is the 1.44 Mb floppy disk drive and I dare
  852. say that this will be true in the majority of cases.
  853.  
  854.  
  855. The assembler used
  856. ------------------
  857. As usual, I have written this code in A86 format. Until now, not many
  858. aspects of the A86 extensions have been used, but, believe me, in future
  859. articles this will be done.
  860.  
  861. A86 is particularly useful for people that make syntax errors. It will
  862. insert the errormessages into the sourcefile so that you can easily find
  863. them back. In the next assembler run the error messages are removed again.
  864.  
  865. To fully use this aspect of A86 programming, I made a small batchfile that
  866. will let me choose between several options while writing the code. Below
  867. you can see the file. After an error, I choose to go back into the editor.
  868. When there are no errors, I might decided to do a trial run. Or to quit to
  869. DOS.
  870.  
  871. This is all done by means of the WACHT command which waits for a keypress.
  872. It returns (in errorlevel) the indexed position in the command tail table
  873. of th key which was pressed.
  874.  
  875.  
  876. Rapid assembly prototyping.
  877. ---------------------------
  878. For easy processing and running sourcefiles I use a small batchfile, which
  879. looks like:
  880.  
  881. ----------- Run.Bat --------------------------------------- Start ---------
  882. @echo off
  883. if "%1" == "" goto leave
  884.  
  885. :start
  886.    ed %1.a86
  887.    a86 %1.a86 %2 %3 %4 %5 %6
  888.  
  889. :menu
  890.    Echo *
  891.    Echo Options:
  892.    Echo *Escape = stop
  893.    Echo *     L = LIST
  894.    echo *  ;-() = back to the editor
  895.    echo * space = test-run of %1.com
  896.    echo *Period = debugger-run with %1.com/sym
  897.  
  898.    wacht  .\=-[]';-()/":?><{}|+_LCE
  899.  
  900. if errorlevel 27 goto start
  901. if errorlevel 26 goto screen
  902. if errorlevel 25 goto list
  903. if errorlevel  4 goto start
  904. if errorlevel  3 goto debugger
  905. if errorlevel  2 goto execute
  906. if errorlevel  1 exit
  907. goto menu
  908.  
  909. :execute
  910.   %1
  911.   if errorlevel 9 echo Errorlevel = 9+
  912.   if errorlevel 8 echo Errorlevel = 8
  913.   if errorlevel 7 echo Errorlevel = 7
  914.   if errorlevel 6 echo Errorlevel = 6
  915.   if errorlevel 5 echo Errorlevel = 5
  916.   if errorlevel 4 echo Errorlevel = 4
  917.   if errorlevel 3 echo Errorlevel = 3
  918.   if errorlevel 2 echo Errorlevel = 2
  919.   if errorlevel 1 echo Errorlevel = 1
  920.   goto menu
  921.  
  922. :debugger
  923.   vgamode 3
  924.   d86 %1
  925.   goto menu
  926.  
  927. :list
  928.   list
  929.   goto menu
  930.  
  931. :screen
  932.   vgamode 3
  933.   goto menu
  934.  
  935. :leave
  936.   echo No file specified
  937. ----------- Run.Bat ---------------------------------------- End ----------
  938.  
  939. This BAT file relies heavily on my computer system. For one, I use DR-DOS 6
  940. which means that I can use the EXIT word to get out of a Batchfile.
  941.  
  942. Also, I switch videomodes back to Mode 3 with "Vgamode 3" and you will have
  943. to use another command for that, like "Mode co80" or using the utillity
  944. that came with your videocard.
  945.  
  946. The program "List" is Vernon Buerg's
  947.  
  948.